package com.jiawa.train.business.config;

import cn.hutool.core.date.DateTime;
import com.jiawa.train.business.domain.SkToken;
import com.jiawa.train.business.enums.RedisKeyPreEnum;
import com.jiawa.train.business.mapper.cust.SkTokenMapperCust;
import com.jiawa.train.business.service.SkTokenService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Configuration
@Slf4j
public class SkTokenSchedulerConfig {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private SkTokenMapperCust skTokenMapperCust;

    @Resource
    private SkTokenService skTokenService;

    @Resource
    private RedissonClient redissonClient;

    @Scheduled(cron = "0 */1 * * * ?")
    public void syncCacheToDB() {
        log.info("开始执行定时任务,在1分后比对缓存和数据库中数据的一致性！");
        //获取所有需要同步的缓存key的数据
        Set<String> ydzKeys = redisTemplate.keys(RedisKeyPreEnum.SK_TOKEN_YDZ_COUNT.getCode() + ":*");
        Set<String> edzKeys = redisTemplate.keys(RedisKeyPreEnum.SK_TOKEN_EDZ_COUNT.getCode() + ":*");
        //同步一等座数据
        if(ydzKeys != null && ydzKeys.size() > 0){
            for (String key : ydzKeys) {
                syncSeatTypeData(key, "1");
            }
        }
        //同步二等座数据
        if(edzKeys != null && edzKeys.size() > 0){
            for (String key : edzKeys) {
                syncSeatTypeData(key, "2");
            }
        }
    }

    //同步执行座位类型的数据信息
    private void syncSeatTypeData(String cacheKey, String seatTypeCode) {
        try{
            String[] split = cacheKey.split(":");
            String dateStr = split[1];
            String trainCode = split[2];
            log.info("开始同步数据: 日期={}, 车次={}", dateStr, trainCode);
            //解析日期
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            //使用simpleDateFormat解析日期字符串,将其转为日期对象
            Date date = sdf.parse(dateStr);
            //获取缓存中的令牌数
           Object cacheCountObj =  redisTemplate.opsForValue().get(cacheKey);
           if(cacheCountObj == null) {
               return;
           }
           int cacheCount = Integer.parseInt(cacheCountObj.toString());
           //查询数据库中是否有该条数据
            SkToken  skToken  = skTokenService.querySkTokenByDateAndTrainCode(date, trainCode);
            if(skToken == null) {
                return;
            }
            int dbCount = "1".equals(seatTypeCode) ? skToken.getYdzCount() : skToken.getEdzCount();
            //判断缓存中的令牌数和数据库中的令牌数据是否一致
            if(cacheCount != dbCount){
                //如果缓存中的数据和数据库中的数据不一致则将缓存中的数据存入数据中
                log.info("检测到数据不一致，开始同步: 日期={}, 车次={}, 座位类型={}, 缓存值={}, 数据库值={}",
                        dateStr, trainCode, seatTypeCode, cacheCount, dbCount);
                //使用Redission锁保证数据的一致性
                RLock lock = null;
                String lockKey = RedisKeyPreEnum.RESOURCE_SK_TOKEN.getCode() + ":" + dateStr + ":" + trainCode;
                lock = redissonClient.getLock(lockKey);
                try{
                    boolean isLock = lock.tryLock(0, 10, TimeUnit.SECONDS);
                    if(isLock){
                        log.info("获取资源锁成功！开始执行数据同步！");
                        DateTime now = DateTime.now();
                        //将缓存中的数据更新到数据库中
                        int updateCount = skTokenMapperCust.updateCount(skToken.getId(), seatTypeCode, cacheCount, now);
                        if(updateCount > 0){
                            log.info("同步成功: 日期={}, 车次={}, 座位类型={}, 新值={}",
                                    dateStr, trainCode, seatTypeCode, cacheCount);
                        }
                        else {
                            log.warn("同步失败: 日期={}, 车次={}, 座位类型={}",
                                    dateStr, trainCode, seatTypeCode);
                        }
                    }
                }catch (Exception e){
                    log.error("获取锁异常！");
                }finally {
                    if(lock != null && lock.isLocked() && lock.isHeldByCurrentThread()){
                        log.info("释放资源锁！");
                        lock.unlock();
                    }
                }
            }
            else{
                log.info("数据一致，无需同步: 日期={}, 车次={}, 座位类型={}",
                        dateStr, trainCode, seatTypeCode);
            }
        }catch(Exception e){
            log.error("解析日期失败！");
        }
    }
    /**
     * 每30分钟执行一次的任务，清理过期缓存
     */
    @Scheduled(cron = "0 */30 * * * ?")
    public void cleanExpiredCache() {
        log.info("每隔30分钟开始执行过期缓存清理任务...");

        // 获取所有计数器key
        Set<String> counterKeys = redisTemplate.keys(RedisKeyPreEnum.SK_TOKEN_YDZ_QUERY_COUNTER.getCode() + "-*");
        Set<String> counterKeys2 = redisTemplate.keys(RedisKeyPreEnum.SK_TOKEN_EDZ_QUERY_COUNTER.getCode() + "-*");

        if (counterKeys != null) {
            counterKeys.addAll(counterKeys2);
        } else {
            counterKeys = counterKeys2;
        }

        // 清理过期计数器
        if (counterKeys != null) {
            for (String key : counterKeys) {
                Long ttl = redisTemplate.getExpire(key);
                if (ttl != null && ttl < 0) {
                    redisTemplate.delete(key);
                    log.info("清理过期计数器: {}", key);
                }
            }
        }

        log.info("过期缓存清理任务完成");
    }
}


